
#include <sys/stat.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include "Defines.h"
#include "Facade.h"
#include "panda.h"
#include "Threads.h"
#include "Debug.h"

/* need this to get IOV_MAX on some platforms. */
/*
#ifndef __need_IOV_MAX
#define __need_IOV_MAX
#endif

#ifndef IOV_MAX
#if defined(__FreeBSD__) || defined(__APPLE__)
# define IOV_MAX 1024
#endif
#endif
*/

conn * Network :: conn_new(const int sfd, const int init_state, const int event_flags, const int read_buffer_size, const bool is_udp, struct event_base *base) 
{
    Facade *pR = Facade::Instance();
	conn *c = pR->GetThreads()->conn_from_freelist();
	if (NULL == c) {
		if (!(c = (conn *)calloc(1, sizeof(conn)))) {
			fprintf(stderr, "calloc()\n");
			return NULL;
		}

	DEBUG2(pthread_self(), " create new connection");
	// MEMCACHED_CONN_CREATE(c);
        c->rbuf = c->wbuf = 0;
      //  c->suffixlist = 0;
       // c->hdrbuf = 0;

        c->rsize = read_buffer_size;
        c->wsize = DATA_BUFFER_SIZE;
     	DEBUG4("malloc rsize=", c->rsize," and wsize = ", c->wsize);
        c->rbuf = (char *)g_malloc((size_t)c->rsize);
        c->wbuf = (char *)g_malloc((size_t)c->wsize);

        if (c->rbuf == 0 || c->wbuf == 0 ) {
            conn_free(c);
            fprintf(stderr, "malloc()\n");
            return NULL;
        }

    }

    c->sfd = sfd;
    // c->udp = is_udp;
    c->state = init_state;
    c->rbytes = c->wbytes = 0;
    c->wcurr = c->wbuf;
    c->rcurr = c->rbuf;

    c->write_and_go = conn_read;
    c->write_and_free = 0;
    c->noreply = false;

    event_set(&c->event, sfd, event_flags, Thread_Event_Handler, (void *)c);
    event_base_set(base, &c->event);
    c->ev_flags = event_flags;

    if (event_add(&c->event, 0) == -1) {
        if ( pR->GetThreads()->conn_add_to_freelist(c)) {
            conn_free(c);
        }
        perror("event_add");
        return NULL;
    }
    // MEMCACHED_CONN_ALLOCATE(c->sfd);
    return c;
}

/*
 * 初始化 200 个全局的网络连接对象
 */
bool Network :: conn_init(void) 
{
    freetotal = 200;
    freecurr = 0;
    if ((freeconns = (conn **)malloc(sizeof(conn *) * freetotal)) == NULL) {
        fprintf(stderr, "malloc()\n");
        return false;
    }
    return true;
}

void Network :: conn_cleanup(conn *c)
{
    assert(c != NULL);
}
/*
 * Frees a connection.
 */
void Network :: conn_free(conn *c) 
{
    DEBUG1("free connection(socket)");
    if (c) {
       // if (c->hdrbuf)
       //     free(c->hdrbuf);
        if (c->rbuf)
            g_free(c->rbuf);
        if (c->wbuf)
            g_free(c->wbuf);
        free(c);
    }
}

void Network :: conn_close(conn *c) 
{
	Facade *pR = Facade::Instance();
	DEBUG3(pthread_self()," close conn = ",c->sfd);
	assert(c != NULL);
	/* delete the event, the socket and the conn */
	event_del(&c->event);
	close(c->sfd);
        c->sfd = 0;
	//
	//
	// Accept_New_Connection(true);
	conn_cleanup(c);
	/* if the connection has big buffers, just free it */
	if (c->rsize > READ_BUFFER_HIGHWAT || pR->GetThreads()->conn_add_to_freelist(c)) {
		conn_free(c);
	}

	return;
}

/*
 * read from network as much as we can, handle buffer overflow and connection
 * close.
 * before reading, move the remaining incomplete fragment of a command
 * (if any) to the beginning of the buffer.
 * return 0 if there's nothing to read on the first read.
 */
int Network ::try_read_network(conn *c) 
{
    int gotdata = 0;
    int res;
  //  Facade *pR = Facade::Instance();
    assert(c != NULL);
    c->rcurr = c->rbuf;
    c->rbytes = 0;
 /*  
    if (c->rcurr != c->rbuf) {
        if (c->rbytes != 0)
            memmove(c->rbuf, c->rcurr, c->rbytes);
        c->rcurr = c->rbuf;
    }
*/
    while (1) {
        if (c->rbytes >= c->rsize) {
            char *new_rbuf = (char *)g_realloc(c->rbuf, c->rsize * 2);
            if (!new_rbuf) {
                c->rbytes = 0; /* ignore what we read */
		ERRO<< "SERVER_ERROR out of memory reading request" << endl;
	//	Conn_Set_State(c, conn_closing);
        //        c->write_and_go = conn_closing;
                return 0;
            }
            c->rcurr = c->rbuf = new_rbuf;
            c->rsize *= 2;
        }

        int avail = c->rsize - c->rbytes;
        res = read(c->sfd, c->rbuf + c->rbytes, avail);
        DEBUG2("read string length = ", res);
        if (res > 0) {
            gotdata += res;
            c->rbytes += res;
            if (res == avail) {
                continue;
            } else {
                break;
            }
        }
        if (res == 0) {
            Conn_Set_State(c, conn_closing);
            return gotdata;
        }
        if (res == -1) {
             
            if (errno == EAGAIN || errno == EWOULDBLOCK) break;
            /* Should close on unhandled errors. */
            Conn_Set_State(c, conn_closing);
            return 0;
        }
    }
    return gotdata;
}

int Network :: new_socket(struct addrinfo *ai) 
{
    int sfd;
    int flags;
    if ((sfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1) {
        perror("socket()");
        return -1;
    }

    if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 ||
        fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) {
        perror("setting O_NONBLOCK");
        close(sfd);
        return -1;
    }
    return sfd;
}

/*
 * Sets a socket's send buffer size to the maximum allowed by the system.
 */
void Network :: maximize_sndbuf(const int sfd) 
{
    socklen_t intsize = sizeof(int);
    int last_good = 0;
    int min, max, avg;
    int old_size;

    /* Start with the default size. */
    if (getsockopt(sfd, SOL_SOCKET, SO_SNDBUF, &old_size, &intsize) != 0) {
        return;
    }

    /* Binary-search for the real maximum. */
    min = old_size;
    max = MAX_SENDBUF_SIZE;

    while (min <= max) {
        avg = ((unsigned int)(min + max)) / 2;
        if (setsockopt(sfd, SOL_SOCKET, SO_SNDBUF, (void *)&avg, intsize) == 0) {
            last_good = avg;
            min = avg + 1;
        } else {
            max = avg - 1;
        }
    }

}

int Network :: server_socket(const int port) 
{
    DEBUG1(port);
    int sfd;
    struct linger ling = {0, 0};
    struct addrinfo *ai;
    struct addrinfo *next;
    struct addrinfo hints;
    char port_buf[NI_MAXSERV];
    int error;
    int success = 0;
    int flags =1;
    Facade *pR = Facade::Instance();
    /*
     * the memset call clears nonstandard fields in some impementations
     * that otherwise mess things up.
     */
    memset(&hints, 0, sizeof (hints));
    hints.ai_flags = AI_PASSIVE|AI_ADDRCONFIG;
    hints.ai_family = AF_UNSPEC;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_socktype = SOCK_STREAM;

    snprintf(port_buf, NI_MAXSERV, "%d", port);
    error= getaddrinfo(pR->GetSettings()->inter, port_buf, &hints, &ai);
    if (error != 0) {
	    if (error != EAI_SYSTEM)
		    fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error));
	    else
		    perror("getaddrinfo()");
	    return 1;
    }

    for (next= ai; next; next= next->ai_next) {
    DEBUG1("" );
        conn *listen_conn_add;
        if ((sfd = new_socket(next)) == -1) {
            freeaddrinfo(ai);
            ;
            return 1;
        }
	setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags));
	setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags));
	setsockopt(sfd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling));
	setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags));
          
	if (bind(sfd, next->ai_addr, next->ai_addrlen) == -1) {
		if (errno != EADDRINUSE) {
			perror("bind()");
			close(sfd);
			freeaddrinfo(ai);
			return 1;
		}
		close(sfd);
		continue;
	} else {
		success++;
		if (listen(sfd, 1024) == -1) 
		{
			perror("listen()");
			close(sfd);
			freeaddrinfo(ai);
			return 1;
		}
	}
	if (!(listen_conn_add = conn_new(sfd, conn_listening, EV_READ | EV_PERSIST, 1, false, pR->main_base))) {
		fprintf(stderr, "failed to create listening connection\n");
		exit(EXIT_FAILURE);
	}

	listen_conn_add->next = listen_conn;
	listen_conn = listen_conn_add;
    }
    freeaddrinfo(ai);
    /* Return zero iff we detected no errors in starting up connections */
    return success == 0;
}

int Network :: new_socket_unix(void) 
{
    int sfd;
    int flags;
    if ((sfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
        perror("socket()");
        return -1;
    }

    if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 ||
        fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) {
        perror("setting O_NONBLOCK");
        close(sfd);
        return -1;
    }
    return sfd;
}

int Network :: server_socket_unix(const char *path, int access_mask) 
{
    Facade *pR = Facade::Instance();
    int sfd;
    struct linger ling = {0, 0};
    struct sockaddr_un addr;
    struct stat tstat;
    int flags =1;
    int old_umask;

    if (!path) {
        return 1;
    }

    if ((sfd = new_socket_unix()) == -1) {
        return 1;
    }

    /*
     * Clean up a previous socket file if we left it around
     */
    if (lstat(path, &tstat) == 0) {
        if (S_ISSOCK(tstat.st_mode))
            unlink(path);
    }

    setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags));
    setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags));
    setsockopt(sfd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling));

    /*
     * the memset call clears nonstandard fields in some impementations
     * that otherwise mess things up.
     */
    memset(&addr, 0, sizeof(addr));

    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path, path);
    old_umask=umask( ~(access_mask&0777));
    if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
        perror("bind()");
        close(sfd);
        umask(old_umask);
        return 1;
    }
    umask(old_umask);
    if (listen(sfd, 1024) == -1) {
        perror("listen()");
        close(sfd);
        return 1;
    }
    if (!(listen_conn = conn_new(sfd, conn_listening, EV_READ | EV_PERSIST, 1, false, pR->main_base))) {
        fprintf(stderr, "failed to create listening connection\n");
        exit(EXIT_FAILURE);
    }

    return 0;
}


/*
 * Returns a connection from the freelist, if any. Should call this using
 * conn_from_freelist() for thread safety.
 */
conn * Network :: do_conn_from_freelist() 
{
	conn *c;
	if (freecurr > 0) {
		c = freeconns[--freecurr];
	} else {
		c = NULL;
	}
	return c;
}

/*
 * Adds a connection to the freelist. 0 = success. Should call this using
 * conn_add_to_freelist() for thread safety.
 */
bool Network :: do_conn_add_to_freelist(conn *c) 
{
	if ( freecurr < freetotal) {
		freeconns[ freecurr++] = c;
		return false;
	} else {
		/* try to enlarge free connections array */
		conn **new_freeconns = (conn **)realloc(freeconns, sizeof(conn *) * freetotal * 2);
		if (new_freeconns) {
			freetotal *= 2;
			freeconns = new_freeconns;
			freeconns[freecurr++] = c;
			return false;
		}
	}
	return true;
}

void Network :: conn_listen_accept( bool do_accept)
{
	conn *next;
	for (next = listen_conn; next; next = next->next) {
		if (do_accept) {
			Update_Event(next, EV_READ | EV_PERSIST);
			if (listen(next->sfd, 1024) != 0) {
				perror("listen");
			}
		}
		else {
			Update_Event(next, 0);
			if (listen(next->sfd, 0) != 0) {
				perror("listen");
			}
		}
	}
}

int Network::write_block(conn *c ) //, const char *pval, unsigned vlen)
{
	assert(c != NULL);
        if( c->wbytes <= 0)
        {
              DEBUG3("c->wbytes = ", c->wbytes, ": no data to send!");
              return TRANSMIT_INCOMPLETE;
        }

        if(c->wbytes > c->wsize )
             c->wsize = c->wbytes;

	unsigned res;
	res = send(c->sfd, c->wbuf, c->wbytes, 0);
        c->wcurr = c->wbuf;
        c->wbytes = 0; 
	DEBUG2("send return = ", res);
	if(res == c->wbytes)
		return TRANSMIT_COMPLETE;
	else
		return TRANSMIT_INCOMPLETE;
}
